Using QuickDraw 3D RAVE
This section illustrates how to use QuickDraw 3D RAVE. In particular, it provides source code examples that show how you can
These are examples of operations that an application might need to perform. To learn how to write and register a new drawing engine, see the section "Writing a Drawing Engine," beginning on page 1-23.
- specify a virtual device
- determine which drawing engines are available and what features they have
- create and configure a draw context
- draw objects in a draw context
- use a draw context as an image cache
- use a texture map's alpha channel for transparency or as a blend matte
- render with antialiasing
- Note
- The code examples shown in this section provide only very rudimentary error handling.
![]()
Specifying a Virtual Device
You send all drawing commands to a draw context. To create a draw context, you need to specify a virtual device and a drawing engine. This section shows how to initialize a virtual device. See the next section for information on specifying a drawing engine.On Macintosh computers, a virtual device represents either an area of memory, a video device, or an offscreen graphics world. You specify a virtual device by filling in fields of a device structure, defined by the
TQADevice
data type.
typedef struct TQADevice { TQADeviceType deviceType; TQAPlatformDevice device; } TQADevice;ThedeviceType
field indicates the type of virtual device you want to draw into. Currently, you can pass eitherkQADeviceMemory
orkQADeviceGDevice
to select a Macintosh device type. Thedevice
field indicates a platform device data structure, which is either of typeTQADeviceMemory
for memory devices orGDHandle
for graphics devices.
typedef union TQAPlatformDevice { TQADeviceMemory memoryDevice; GDHandle gDevice; } TQAPlatformDevice;To specify a memory device, you fill in the fields of a memory device structure, defined by theTQADeviceMemory
data type.
typedef struct TQADeviceMemory { long rowBytes; TQAImagePixelType pixelType; long width; long height; void *baseAddr; } TQADeviceMemory;Listing 1-1 shows how to initialize a memory device.Listing 1-1 Initializing a memory device
TQADevice myDevice; long myTargetMemory[100][100]; myDevice.deviceType = kQADeviceMemory; myDevice.device.memoryDevice.rowBytes = 100 * sizeof(long); myDevice.device.memoryDevice.pixelType = kQAPixel_ARGB32; myDevice.device.memoryDevice.width = 100; myDevice.device.memoryDevice.height = 100; myDevice.device.memoryDevice.baseAddr = myTargetMemory;Drawing to memory always occurs in the native pixel format of the platform. Note that not all drawing engines support drawing to memory. For information on determining what kinds of virtual devices a particular drawing engine supports, see "Finding a Drawing Engine" (page 1-16).Listing 1-2 shows how to initialize a virtual graphics device.
Listing 1-2 Initializing a graphics device
TQADevice myDevice; GDHandle gDeviceHandle; /*create a GDHandle (perhaps by calling NewGDevice)*/ ... myDevice.deviceType = kQADeviceGDevice; myDevice.device.gDevice = gDeviceHandle;The code in Listing 1-2 assumes that thegDeviceHandle
global variable has been assigned a handle to aGDevice
record. See Inside Macintosh: Imaging with QuickDraw for complete information on creating and configuring graphics devices.
- Note
- A draw context can be associated with only a single virtual device and hence with only a single
GDevice
. Macintosh windows can straddle several screens, each associated with a differentGDevice
. It is your responsibility to determine which graphics devices a window straddles and to create a separate draw context for each one.![]()
Finding a Drawing Engine
Not all drawing engines are capable of drawing into all type of virtual devices. For example, some drawing engines might not support memory devices at all, and other drawing engines might support only a particular graphics device. As a result, once you've initialized a virtual device, you need to find a drawing engine that is capable of drawing into that device. You do this by finding the available drawing engines and selecting one that is capable of drawing into the desired virtual device. If more than one engine supports that device, you need to choose one of them.QuickDraw 3D versions 1.1 and later provide a control panel that allows the user to select the drawing engine to use for each available monitor.
You can search through the list of available drawing engines by calling the
QADeviceGetFirstEngine
andQADeviceGetNextEngine
functions. TheQADeviceGetFirstEngine
function returns the preferred drawing engine for the specified device; in most cases, this engine is the best engine to use for high performance rendering. However, you might need specific drawing features that are not supported by the preferred drawing engine. If so, you can use theQAEngineGestalt
function to query the engine's capabilities, as shown in Listing 1-3.Listing 1-3 Finding a drawing engine with fast texture mapping
TQAEngine *MyFindPreferredEngine (TQADevice *device) { TQAEngine *myEngine; unsigned long fast; for (myEngine = QADeviceGetFirstEngine(device); myEngine; myEngine = QADeviceGetNextEngine(device, myEngine)) { if (QAEngineGestalt(myEngine, kQAGestalt_FastFeatures, &fast) == kQANoErr) { if (fast & kQAFast_Texture) return(myEngine); } } return(NULL); }TheMyFindPreferredEngine
function defined in Listing 1-3 calls theQADeviceGetFirstEngine
function to get the preferred drawing engine for the specified device. Then it callsQAEngineGestalt
, passing thekQAGestalt_FastFeatures
selector, to determine which (if any) features are accelerated by that engine. If the engine supports accelerated texture mapping, theMyFindPreferredEngine
function returns that drawing engine. Otherwise, theMyFindPreferredEngine
function loops through all engines capable of drawing into the specified device until it finds one that does support fast texture mapping. If none is found,MyFindPreferredEngine
returns the valueNULL
.
- Note
- See "Gestalt Selectors" (page 1-57) for a complete description of the selectors you can pass to the
QAEngineGestalt
function.![]()
Creating and Configuring a Draw Context
Once you've initialized a virtual device and selected a drawing engine capable of drawing to that device, you can call theQADrawContextNew
function to create a new draw context. You pass the device and engine to that function, along with a drawing rectangle, a clipping region, and a set of draw context flags. The flags specify features of the new draw context. Listing 1-4 illustrates how to create a double-buffered draw context with z buffering.Listing 1-4 Creating a draw context
TQADrawContext *myDrawContext; if (QADrawContextNew(&myDevice, &myRect, &myClip, myEngine, kQAContext_DoubleBuffer, &myDrawContext) != kQANoErr) { /*Error! Could not create new draw context.*/ }IfQADrawContextNew
succeeds, it returns the result codekQANoErr
and sets themyDrawContext
parameter to the new draw context. Otherwise, if an error occurs,QADrawContextNew
returns some other result code and sets themyDrawContext
parameter to the valueNULL
.
QuickDraw 3D RAVE does not provide a function to reposition an existing draw context. If a window associated with a draw context is moved on the screen, you need to delete the existing draw context and create a new draw context at the new location. Similarly, QuickDraw 3D RAVE does not provide a function to change the clipping region of a draw context. If you want to change a clipping region, you need to delete the existing draw context and create a new draw context with the desired clipping region.
- Note
- When you are finished using the new draw context, you should free the memory and other resources it uses by calling the
QADrawContextDelete
function.![]()
However, you can change a number of other features of a draw context without having to delete an existing draw context and create a new one. The features you can change are indicated by the state variables of the draw context. For example, to change the background color of a draw context to opaque black, you can use the code shown in Listing 1-5.
Listing 1-5 Setting a draw context state variable
void MySetBackgroundToBlack (TQADrawContext *drawContext); { QASetFloat(drawContext, kQATag_ColorBG_a, 1.0); QASetFloat(drawContext, kQATag_ColorBG_r, 0.0); QASetFloat(drawContext, kQATag_ColorBG_g, 0.0); QASetFloat(drawContext, kQATag_ColorBG_b, 0.0); }TheQASetFloat
function sets a draw context state variable that has a floating-point value. QuickDraw 3D RAVE provides functions to get and set state variables with floating-point, long integer, or pointer values.
The
- Note
- See "Tags for State Variables," beginning on page 1-38, for a complete description of the available draw context state variables.
![]()
QASetFloat
function is defined using a C language macro:
#define QASetFloat(drawContext,tag,newValue) \ (drawContext)->setFloat (drawContext,tag,newValue)During compilation, theQASetFloat
call is replaced by code that directly calls the drawing engine's floating-point setting method. This allows you to achieve the highest possible performance when configuring a draw context.Drawing in a Draw Context
QuickDraw 3D RAVE allows you to draw five kinds of objects in a draw context: points, lines, triangles, triangle meshes, and bitmaps. You draw by calling a function to draw the desired type of object. For instance, to draw a single point, you can call theQADrawPoint
function, as follows:
QADrawPoint(myDrawContext, myPoint);Here, themyPoint
parameter specifies the point to draw. All objects that a drawing engine can draw (except for bitmaps) are defined by points or vertices. QuickDraw 3D RAVE supports two different types of vertices: Gouraud vertices and texture vertices. You use Gouraud vertices for drawing Gouraud-shaded triangles, and also for drawing points and lines. A Gouraud vertex is defined by theTQAVGouraud
data structure, which specifies the position, depth, color, and transparency information.You use texture vertices to define triangles to which a texture is to be mapped. A texture vertex is defined by the
TQAVTexture
data structure, which specifies the position, depth, transparency, and texture mapping information.
- IMPORTANT
- QuickDraw 3D RAVE does not currently support clipping to a draw context. All triangles and other objects drawn to a draw context must lie entirely within the draw context.
![]()
Using a Draw Context as a Cache
QuickDraw 3D RAVE supports draw context caching, a technique that allows you to improve rendering performance when a large number of the objects in a scene don't change from frame to frame. A draw context cache is simply a draw context that contains an image and is designated as the initial context in a call toQARenderStart
. The contents of that context are drawn into the destination draw context before any other objects.To create a draw context cache, you first create a draw context by calling the
QADrawContextNew
function, where theflags
parameter has theQAContext_Cache
flag set. Then you draw the unchanging objects into the draw context cache. For example, suppose that you want to draw a series of frames in which two triangles remain constant from frame to frame but a third triangle changes every frame. Listing 1-6 shows how to do this.Listing 1-6 Creating and using a draw context cache
TQAVGouraud tri1[3], tri2[3], tri3[3]; TQADrawContext *myCache, *myDest; /*Create draw context cache and destination draw context.*/ QADrawContextNew(myDev, rect, NULL, myEng, QAContext_Cache, &myCache); QADrawContextNew(myDev, rect, NULL, myEng, QAContext_DoubleBuffer, &myDest); /*Set up the image in the cache context.*/ QARenderStart(myCache, NULL, NULL); QADrawTriGouraud(myCache, &tri1[0], &tri1[1], &tri1[2], kQATriFlags_None); QADrawTriGouraud(myCache, &tri2[0], &tri2[1], &tri2[2], kQATriFlags_None); QARenderEnd(myCache, NULL); /*Render frames using the cache and moving tri3 only.*/ while (gStillMovingTriangle3) { MyMoveTri(tri3); QARenderStart(myDest, NULL, myCache); QADrawTriGouraud(myDest, &tri3[0], &tri3[1], &tri3[2], kQATriFlags_None); QARenderEnd(myDest, NULL); }Not all drawing engines support draw context caching. If a drawing engine does not support caching, it should return the valueNULL
whenever you pass theQAContext_Cache
flag toQADrawContextNew
.
- IMPORTANT
- All draw context caches must be single buffered, and they must be created using the same device and rectangle as the destination draw contexts with which they will be used.
![]()
Using a Texture Map Alpha Channel
Texture maps whose pixel type is eitherkQAPixel_ARGB16
orkQAPixel_ARGB32
contain an alpha channel value for each pixel in the map. You can use the alpha channel value to control the transparency of an object on a pixel-by-pixel basis, or you can use the alpha channel value as a blend matte that exposes only certain portions of an image.To use the alpha channel to control transparency, you should set the drawing engine's transparency blending mode to
kQABlend_Premultiply
. (You specify an engine's transparency blending mode by assigning a value to itskQATag_Blend
state variable.) For pixels of typekQAPixel_ARGB16
, the alpha channel value occupies bit 15; when the value is 1, the pixel is opaque; when the value is 0, the pixel is completely transparent. For pixels of typekQAPixel_ARGB32
, the alpha channel value occupies bits 31 through 24; when the value is 255, the pixel is opaque; when the value is 0, the pixel is completely transparent.
Note that the specular highlight is unaffected by the diffuse transparency of an object. As a result, setting an object's alpha channel value to 0 when using the
- IMPORTANT
- The
kQABlend_Premultiply
transparency model assumes that the diffuse color of a pixel has been premultiplied by the alpha channel value. As a result, every pixel of the texture map must be premultiplied by its associated alpha channel value before you create the texture map by callingQATextureNew
.![]()
kQABlend_Premultiply
transparency blending mode does not cause the object to vanish. The specular highlight is still rendered.To use the alpha channel as a blend matte to cut out certain portions of a rendered object, you should set the drawing engine's transparency blending mode to
kQABlend_Interpolate
. If the alpha channel value of all pixels in an object is 0, neither the object nor its specular highlight is rendered. This effectively eliminates the object from the rendered image.
The
- IMPORTANT
- The
kQABlend_Interpolate
transparency model assumes that the diffuse color of a pixel has not been premultiplied by the alpha channel value. This multiplication is performed by the blending operation.![]()
kQABlend_Interpolate
blending mode cannot render a transparent surface as accurately as thekQABlend_Premultiply
mode, because the specular highlight is scaled by the alpha value. In some cases, you can compensate for this behavior by increasing the brightness of the specular highlight.Rendering With Antialiasing
A drawing engine may support an antialiasing mode that determines the kind of antialiasing applied to a drawing context. (Antialiasing is the smoothing of jagged edges on a displayed shape by modifying the transparencies of individual pixels along the shape's edge.)
You specify an engine's antialiasing mode by assigning a value to its
- Note
- The antialiasing mode state variable is optional; it must be supported only when a drawing engine supports the
kQAOptional_Antialias
feature.![]()
kQATag_Antialias
state variable. QuickDraw 3D RAVE provides these constants for antialiasing modes:
#define kQAAntiAlias_Off 0 #define kQAAntiAlias_Fast 1 #define kQAAntiAlias_Mid 2 #define kQAAntiAlias_Best 3The interpretation of these values is specific to each drawing engine. For example, a drawing engine might be able to support antialiased line drawing with no performance penalty but that same engine might incur a 50 percent slowdown when drawing antialiased triangles. Accordingly, this engine might interpret thekQAAntiAlias_Fast
antialiasing mode as rendering antialiased lines only, and it might interpret thekQAAntiAlias_Mid
mode as rendering both antialiased lines and triangles.
- Note
- QuickDraw 3D RAVE interprets antialiasing modes independently of the transparency blending modes, unlike some other rendering technologies. For instance, with OpenGL you must select specific blending modes when antialiasing is enabled.
![]()